#include "rumr.h"


Rumr::Rumr(long double workload_size, long double error_rate) : SchedulingHeuristic( error_rate) {
	this->workload_size = workload_size;
	this->name = "RUMR";
}

Rumr::~Rumr() {}


long double Rumr::schedule(Grid *grid, Job *job) {
	
	fprintf(stdout,"Using RUMR Heuristic...\n");
	
	this->grid = grid;
	this->job = job;
	
	this->reinicialize();
	
	fprintf(stdout, "Calculating number of iterations...\n");
	this->calculate_number_of_iterations(grid, job);
	fprintf(stdout, "Number of iterations = %d\n", number_of_iterations);
	
	this->calculate_all_initial_chunk_size(grid, job);
	
	fprintf(stdout,"At begining of schedule method. The number of processors is %d\n", number_of_processors);
	
	ChunkMap::iterator it;
	for(it = initial_chunks.begin(); it != initial_chunks.end(); it++) {
		long double task_size = it->second;
		job->addTask( task_size, task_size / 10 );
	}
	
	fprintf(stdout, "Starting first round with workload computer = %Lf\n", workload_computed);
	
	//First Round
	this->schedule_first_round(grid, job);
	
	fprintf(stdout, "Finished first round with workload computer = %Lf\n", workload_computed);
	fprintf(stdout, "Starting UMR Phase with workload computer = %Lf\n", workload_computed);
	
	//Schedule umr phase
	this->schedule_umr_phase(grid, job);
	
	fprintf(stdout, "Finished UMR Phase with workload computer = %Lf\n", workload_computed);
	fprintf(stdout, "Starting Weighted Factory Phase with workload computer = %Lf\n", workload_computed);
	
	//Switch to Weighted Factory Phase
	this->phase = WF;
	
	//Schedule wf pahse (Weighted Factory)
	this->schedule_wf_phase(grid, job);
	
	fprintf(stdout, "Finished Weighted Factory Phase with workload computer = %Lf\n", workload_computed);
	
	this->doSimGridSimulation(SG_ALL_TASKS);
	
	/*Makespan for this execution*/
	long double makespan = SG_getClock();
	
	fprintf(stdout, "Finished scheduling process with makespan = %Lf\n", makespan);		
	
	this->job->deleteAllTasks();

	return makespan;
}
		

void Rumr::doSimGridSimulation(SG_Task task_to_watch) {
	tasks_finished = SG_simulate(-1, task_to_watch, SG_VERBOSE);
	
	/*Get task state distribution*/
 	SG_getStateDistribution(&numberOfTasksNotScheduled, &numberOfTasksScheduled, &numberOfTasksReady,
 	&numberOfTasksRunning, &numberOfTasksFailed, &numberOfTasksFinished);
	 	
 	fprintf(stderr,"\n------------Distribution of tasks-------------");
 	fprintf(stderr,"\nNot Scheduled=%d", this->numberOfTasksNotScheduled);
 	fprintf(stderr,"\nScheduled=%d", this->numberOfTasksScheduled);	
 	fprintf(stderr,"\nReady=%d", this->numberOfTasksReady);
 	fprintf(stderr,"\nRunning=%d", this->numberOfTasksRunning);
 	fprintf(stderr,"\nFailed=%d", this->numberOfTasksFailed);
 	fprintf(stderr,"\nFinished=%d", this->numberOfTasksFinished);
 	fprintf(stderr,"\n----------------------------------------------\n");
	
}


void Rumr::schedule_first_round( Grid *grid, Job *job ) {
	ChunkMap::iterator it;
	for(int t = 0; t < job->getNumberOfTasks(); t++) {
		Task *task = job->getTaskByIndex(t);
		if(!task->notScheduled()) continue;
		for(it = initial_chunks.begin(); it != initial_chunks.end(); it++) {
			const char* proc_name = it->first;
			long double task_size = it->second;
			if(task_size == task->getDataInput()->getSize()) {
				int site_index, proc_index;
				sscanf(proc_name, "processor_%d_site%d", &proc_index, &site_index);
				this->assign( t, (site_index-1), (proc_index-1) );
				
				fprintf(stdout,"At first round. Assign processor %s to task %d with size %Lf\n", proc_name, t, task_size);
				
				this->workload_computed += task_size;
				processor_round[ proc_name ]++;

				bool not_exit = true;
				do {
					this->doSimGridSimulation(SG_ANY_TASK);
					for (int n = 0; this->tasks_finished[n]; n++) {
						if ((SG_getTaskType(this->tasks_finished[n])==SG_TRANSFER) && 
								(SG_getTaskParents(this->tasks_finished[n])[0]== NULL)) {
							not_exit = false;
							break;
						}
					}
				} while(not_exit);
				break;
			}
		}
	}

}


void Rumr::schedule_umr_phase( Grid *grid, Job *job ) {
	ChunkMap::iterator it;
//	while( this->workload_computed < this->workload_size * MODIFY_PHASE ) {
	for(int i = 2; i <= number_of_iterations; i++) {
		for(it = initial_chunks.begin(); it != initial_chunks.end(); it++) {
			Processor *proc = grid->getProcessorByName( it->first );
			int site_index, proc_index;
			sscanf(proc->getName(), "processor_%d_site%d", &proc_index, &site_index);
			Link *link = grid->getBottleneckLinkOfRouteToSite(site_index-1);
			long double speed_link_temp = this->getPrediction(link->getBandwidthAvailable(-1), link->getBandwidthError(-1));
			long double speed_processor_temp = job->getInputTpt() * proc->getCpuSpeed() * this->getPrediction(proc->getCpuAvailable(-1), proc->getCpuAvailableError(-1));
			long double task_size = this->calculate_next_chunk_size( initial_chunks[proc->getName()], speed_link_temp, speed_processor_temp,
												number_of_processors, (int)processor_round[proc->getName()] );
			
			processor_round[ proc->getName() ]++;
			job->addTask( task_size, task_size / 10 );
			for(int t = 0; t < job->getNumberOfTasks(); t++) {
				Task *task = job->getTaskByIndex(t);
				if( task->notScheduled() ) {
					this->assign( t, (site_index-1), (proc_index-1) );
					this->workload_computed += task_size;
					
					fprintf(stdout,"At UMR phase. Assign processor %s to task %d with size %Lf\n", proc->getName(), t, task_size);
					
					bool not_exit = true;
					do {
						this->doSimGridSimulation(SG_ANY_TASK);
						for (int n = 0; this->tasks_finished[n]; n++) {
							if ((SG_getTaskType(this->tasks_finished[n])==SG_TRANSFER) && 
									(SG_getTaskParents(this->tasks_finished[n])[0]== NULL)) {
								not_exit = false;
								break;
							}
						}
					} while(not_exit);
					break;
				}
			}
			
			if( this->workload_computed >= this->workload_size * MODIFY_PHASE )
				break;
		}
	}
	
}


void Rumr::schedule_wf_phase( Grid *grid, Job *job ) {

	long double sum_proc_speed = this->sum_processors_speed(grid, job->getInputTpt());
	
	fprintf(stdout,"At Weighted Factory phase. The sum of all processors speed is %Lf\n", sum_proc_speed);
	fprintf(stdout,"Begin Weighted Factory phase with workload computed =  %Lf and workload size = %Lf\n", workload_computed, workload_size);
	
	while( (this->workload_computed) < this->workload_size ) {
		
		fprintf(stdout,"At Weighted Factory phase. Begin a new round with workload computed =  %Lf and workload size = %Lf\n", workload_computed, workload_size);
		
		long double workload_iteration = (this->workload_size - this->workload_computed) / 2;

		fprintf(stdout,"At Weighted Factory phase. workload iteration =  %Lf \n", workload_iteration);
		
		ChunkMap::iterator it;
		for(it = initial_chunks.begin(); it != initial_chunks.end(); it++) {
			Processor *proc = grid->getProcessorByName( it->first );
			long double task_size = (proc->getCpuSpeed() * this->getPrediction(proc->getCpuAvailable(-1), proc->getCpuAvailableError(-1)) / sum_proc_speed) * workload_iteration;
			if(task_size < 1.0) task_size = 1.0;
			int site_index, proc_index;
			sscanf(proc->getName(), "processor_%d_site%d", &proc_index, &site_index);
			job->addTask( task_size, task_size / 10 );
			for(int t = 0; t < job->getNumberOfTasks(); t++) {
				Task *task = job->getTaskByIndex(t);
				if( task->notScheduled() ) {
					this->assign( t, (site_index-1), (proc_index-1) );
					this->workload_computed += task_size;
					
					fprintf(stdout,"At Weighted Factory phase. Assign processor %s to task %d with size %Lf\n", proc->getName(), t, task_size);
					
					break;
				}
			}
		}
		
		while (grid->isTotallyBusy()) {
			this->doSimGridSimulation(SG_ANY_TASK);
		}
		
	}

}


long double Rumr::calculate_next_chunk_size( long double chunk_initial, long double speed_link, long double speed_processor,
									int number_of_processors, int number_of_iteration ) {
	
	long double ratio = speed_link / (number_of_processors * speed_processor);
	long double pow_result = powl( ratio, number_of_iteration );
	if( pow_result < 1.0 ) pow_result = 1.1;
	long double result = pow_result * chunk_initial;
	
	fprintf(stderr, "----> calculate_next_chunk_size()::speed_link = %Lf \n", speed_link);
	fprintf(stderr, "----> calculate_next_chunk_size()::speed_proc = %Lf \n", (number_of_processors * speed_processor));
	fprintf(stderr, "----> calculate_next_chunk_size()::pow_result = %Lf \n", pow_result);
	
	return result;

}

	
long double Rumr::calculate_initial_chunk_size( long double speed_link, long double speed_processor, int number_of_processors,
												long double workload_total ) {
	

	long double ratio = speed_link / (number_of_processors * speed_processor);
	long double numerator = (1 - ratio) * workload_total;
	long double pow_result = powl( ratio , this->number_of_iterations );
	long double denominator = number_of_processors * ( 1 - pow_result );

	return numerator / denominator;

}


void Rumr::calculate_all_initial_chunk_size(Grid *grid, Job *job) {
	number_of_processors = this->count_processors(grid);
	for(int i = 0; i < grid->getNumberOfSites(); i++) {
		Site *site = grid->getSiteByIndex(i);
		for(int j = 0; j < site->getNumberOfProcessors(); j++) {
			Processor *p = site->getProcessorByIndex(j);			
			Link *link = grid->getBottleneckLinkOfRouteToSite(i);
			long double speed_link_temp = this->getPrediction(link->getBandwidthAvailable(-1), link->getBandwidthError(-1));			
			long double speed_processor_temp  = job->getInputTpt() * p->getCpuSpeed() * this->getPrediction(p->getCpuAvailable(-1), p->getCpuAvailableError(-1));
			long double initial_chunk = this->calculate_initial_chunk_size( speed_link_temp, speed_processor_temp, number_of_processors, workload_size );
/* ---> */	initial_chunks[ p->getName() ] = initial_chunk / 10;
			processor_round[ p->getName() ] = 0;
		}
	}
}

long double Rumr::sum_processors_speed(Grid *grid, long double tpt) {
	long double sum = 0;
	for(int i = 0; i < grid->getNumberOfSites(); i++) {
		Site *site = grid->getSiteByIndex(i);
		for(int j = 0; j < site->getNumberOfProcessors(); j++) {
			Processor *proc = site->getProcessorByIndex(j);
			sum += proc->getCpuSpeed() * this->getPrediction(proc->getCpuAvailable(-1), proc->getCpuAvailableError(-1));
		}
	}
	return sum;
}

int Rumr::count_processors(Grid *grid) {
	int count = 0;
	for(int i = 0; i < grid->getNumberOfSites(); i++) {
		Site *site = grid->getSiteByIndex(i);
		count += site->getNumberOfProcessors();
	}
	return count;
}


void Rumr::calculate_number_of_iterations(Grid *grid, Job *job) {
	number_of_processors = this->count_processors(grid);
//	long double teta = get_teta(grid, job->getInputTpt());
//	long double sum_alpha_bi = get_sum_alpha_bi(grid, job->getInputTpt());
//	long double negative_workload_size = workload_size * (-1);
//	number_of_iterations = 0;
//	long double x = 1;
//	
//	do {
//		number_of_iterations++;
//		long double pow_teta = powl(teta,number_of_iterations);
//		long double ln_teta = log(teta);
//		
//		long double numerator = negative_workload_size * pow_teta * ln_teta * sum_alpha_bi;
//		long double denominator = 1 - pow_teta;
//		x = numerator / denominator;
//		fprintf(stderr, " >>>>>> number_of_iterations = %d <<<<<<\n", number_of_iterations);
//		fprintf(stderr, " >>>>>> pow_teta = %Lf <<<<<<\n", pow_teta);
//		fprintf(stderr, " >>>>>> ln_teta = %Lf <<<<<<\n", ln_teta);
//		fprintf(stderr, " >>>>>> numerator = %Lf <<<<<<\n", numerator);
//		fprintf(stderr, " >>>>>> denominator = %Lf <<<<<<\n", denominator);
//		fprintf(stderr, " >>>>>> x = %Lf <<<<<<\n", x);
//	} while(x > 0.0001);
	
	Link *link = grid->getBottleneckLinkOfRouteToSite(0);
	long double speed_link = this->getPrediction(link->getBandwidthAvailable(-1), link->getBandwidthError(-1));
	long double speed_proc = number_of_processors * job->getInputTpt() * sum_processors_speed(grid, job->getInputTpt());
	long double x = 1;
	number_of_iterations = 0;
	
	fprintf(stderr, " >>>>>> speed_link = %Lf <<<<<<\n", speed_link);
	fprintf(stderr, " >>>>>> speed_proc = %Lf <<<<<<\n", speed_proc);
	
	do {
		number_of_iterations++;
		long double ratio = speed_link / speed_proc;
		long double pot = powl(ratio,number_of_iterations);
		long double first = workload_size / (1 - pot);
		long double second = pot;
		long double third = log(ratio);
		x = first * second * third;
//		x /= 100000;
		
		fprintf(stderr, "\n >>>>>> number_of_iterations = %d <<<<<<\n", number_of_iterations);
		fprintf(stderr, " >>>>>> ratio = %Lf <<<<<<\n", ratio);
		fprintf(stderr, " >>>>>> pot = %Lf <<<<<<\n", pot);
		fprintf(stderr, " >>>>>> first = %Lf <<<<<<\n", first);
		fprintf(stderr, " >>>>>> third = %Lf <<<<<<\n", third);
		fprintf(stderr, " >>>>>> x = %Lf <<<<<<\n", x);
		
	} while(x < -0.1);

//	number_of_iterations = 15;
	
	fprintf(stderr, " >>>>>> iterations = %d <<<<<<\n\n", number_of_iterations);
}

long double Rumr::get_teta(Grid *grid, long double tpt) {
	long double sum = 0;
	for(int i = 0; i < grid->getNumberOfSites(); i++) {
		Site *site = grid->getSiteByIndex(i);
		Link *link = grid->getBottleneckLinkOfRouteToSite(i);
		for(int j = 0; j < site->getNumberOfProcessors(); j++) {
			Processor *proc = site->getProcessorByIndex(j);
			long double speed_proc = tpt * proc->getCpuSpeed() * this->getPrediction(proc->getCpuAvailable(-1), proc->getCpuAvailableError(-1));
			long double speed_link = this->getPrediction(link->getBandwidthAvailable(-1), link->getBandwidthError(-1));
			sum += ( speed_proc / speed_link );
		}
	}
	
	return (1 / sum);
}

long double Rumr::get_sum_alpha_bi(Grid *grid, long double tpt) {
	long double sum = 0;
	for(int i = 0; i < grid->getNumberOfSites(); i++) {
		Site *site = grid->getSiteByIndex(i);
		Link *link = grid->getBottleneckLinkOfRouteToSite(i);
		for(int j = 0; j < site->getNumberOfProcessors(); j++) {
			Processor *proc_temp = site->getProcessorByIndex(j);
			long double alpha = this->get_alpha( grid, proc_temp, tpt );
			long double speed_link = this->getPrediction(link->getBandwidthAvailable(-1), link->getBandwidthError(-1));
			sum += ( alpha / speed_link );
		}
	}
	
	return sum;
}

long double Rumr::get_alpha(Grid *grid, Processor *proc, long double tpt) {
	long double sum = 0;
	for(int i = 0; i < grid->getNumberOfSites(); i++) {
		Site *site = grid->getSiteByIndex(i);
		for(int j = 0; j < site->getNumberOfProcessors(); j++) {
			Processor *proc_temp = site->getProcessorByIndex(j);
			if( strcmp( proc->getName(), proc_temp->getName() ) )
				continue;
			sum += tpt * proc_temp->getCpuSpeed() * this->getPrediction(proc_temp->getCpuAvailable(-1), proc_temp->getCpuAvailableError(-1));
		}
	}
	
	long double speed_proc = tpt * proc->getCpuSpeed() * this->getPrediction(proc->getCpuAvailable(-1), proc->getCpuAvailableError(-1));;
		
	return (speed_proc / sum);
}

long double Rumr::powl(long double x, int y) {
	long double res = 1;
	for(int i = 1; i <= y; i++) {
		res *= x;
	}
	return res;
}


void Rumr::reinicialize() {
	this->workload_computed = 0;
	this->phase = UMR;
	this->number_of_processors = -1;
	this->tasks_finished = NULL;
}
